// go:build
package notify

import (
	"gomeshcli/comm"
	"gomeshcli/comm/defines"
	"gomeshcli/sconfig"
	"os"
	"strings"
	"sync"
	"time"

	"gitee.com/kimsoso/go-utils/utils"
)

type Snotifies struct {
	sync.RWMutex
	chanNtf chan sconfig.Snotify // 避免系统命令堵塞

	deletePathsMap map[string]bool // 被删除路径的map
	existsPathsMap map[string]bool // 增加路径的map
	ntfs           Ntfs
}

var (
	onceNotifies   sync.Once
	notifiesSingle *Snotifies
)

func NewNotifies() *Snotifies {
	onceNotifies.Do(func() {
		notifiesSingle = &Snotifies{
			chanNtf: make(chan sconfig.Snotify, 100000),

			ntfs:           *NewNtfs(),
			deletePathsMap: map[string]bool{},
			existsPathsMap: map[string]bool{},
		}
		go notifiesSingle.popNtfToNtfs()
	})
	return notifiesSingle
}

// 等待处理的列表长度
func (s *Snotifies) RunningLen() int {
	s.RLock()
	defer s.RUnlock()
	return len(s.ntfs.runningNtfs)
}

// 睡眠
func (s *Snotifies) Sleep() {
	time.Sleep(durationOfNeedProcess)
}

// 新增一条目录改动
func (s *Snotifies) Add(act, path string) {
	ntf := sconfig.Snotify{
		Path:  path,
		Act:   act,
		Ctime: time.Now(),
	}

	s.chanNtf <- ntf
}

// 将通道中的系统命令存入命令序列中
func (s *Snotifies) popNtfToNtfs() {
	for ntf := range s.chanNtf {
		s.Lock()
		s.ntfs.Add(ntf)
		s.Unlock()
	}
}

// 处理系统命令
func (s *Snotifies) ProcessNtfs() {
	s.Lock()
	defer s.Unlock()

	if s.notifiesToRunning() {
		s.doProcessSfiles()
	}
}

// 将文件改动移动到待处理列表中
func (s *Snotifies) notifiesToRunning() (continueProcess bool) {
	continueProcess = false

	// 有命令正在执行，退出
	if len(s.ntfs.runningNtfs) > 0 {
		return
	}

	if len(s.ntfs.queueNtfs) == 0 {
		return
	}

	lastNtf := s.ntfs.queueNtfs[len(s.ntfs.queueNtfs)-1]
	// 命令还比较“新”，退出
	if time.Since(lastNtf.Ctime) <= durationOfNeedProcess {
		return
	}

	err := s.ntfs.MoveFullToRunning()
	if err == nil {
		continueProcess = true
	}

	return
}

func (s *Snotifies) doProcessSfiles() {
	delPaths, existSfiles := s.splitRunning()

	if len(delPaths) > 0 {
		if comm.SendLocalDelete(delPaths, sconfig.SConfig.GetHostPort()) == nil {
			s.ntfs.removeRunnings(s.deletePathsMap)
			s.deletePathsMap = map[string]bool{}
		}
	}

	if len(existSfiles) > 0 {
		upaths, _ := comm.SendLocalExistFile(existSfiles, sconfig.SConfig.GetHostPort())
		s.ntfs.removeRunnings(upaths)
	}
}

// 将待处理文件列表分隔为：已删除、存在文件，并合并重复路径
func (s *Snotifies) splitRunning() (deletePaths []string, existSfiles []*sconfig.Sfile) {
	deletePaths = []string{}
	existSfiles = []*sconfig.Sfile{}

	for _, ntf := range s.ntfs.runningNtfs {
		fi, err := os.Stat(ntf.Path)
		if os.IsNotExist(err) {
			if _, ok := s.deletePathsMap[ntf.Path]; !ok {
				s.deletePathsMap[ntf.Path] = true
			} else {
				continue
			}

			deletePaths = append(deletePaths, ntf.Path)
		} else {
			if _, ok := s.existsPathsMap[ntf.Path]; ok {
				continue
			} else {
				s.existsPathsMap[ntf.Path] = true
			}

			sfile := sconfig.Sfile{
				Path:     ntf.Path,
				IsDelete: false,
				IsDir:    fi.IsDir(),
				Size:     fi.Size(),
			}
			existSfiles = append(existSfiles, &sfile)
			if sfile.IsDir {
				s.attachSubFiles(sfile.Path, &existSfiles)
			}
		}
	}
	s.existsPathsMap = map[string]bool{}
	return
}

func (s *Snotifies) attachSubFiles(path string, existSfiles *[]*sconfig.Sfile) {
	subfiles := utils.GetAllFiles(path)
	for _, path := range subfiles {
		if strings.HasSuffix(path, defines.DirFlagFile) {
			continue
		}

		if _, ok := s.existsPathsMap[path]; !ok {
			s.existsPathsMap[path] = true

			fi, _ := os.Stat(path)

			sfile := &sconfig.Sfile{
				Path:     path,
				IsDelete: false,
				IsDir:    fi.IsDir(),
				Size:     fi.Size(),
			}

			*existSfiles = append(*existSfiles, sfile)
		}
	}
}

func (s *Snotifies) Close() {
	close(s.chanNtf)
}
